home *** CD-ROM | disk | FTP | other *** search
Text File | 1996-10-17 | 19.1 KB | 780 lines | [TEXT/CWIE] |
- /* CTB.c - Handles action specific to the Communications Toolbox
-
- This file can be included in a C project or compiled as a
- library. It allows the programmer to initiate connections
- through a Communications Toolbox connection. In theory,
- It works with any connection tool. It was most rigorously
- tested with the Appletalk ADSP tool.
-
- Assumptions:
- You are running a machine and OS that has/supports
- Communications Toolbox.
- You will not attempt to cram more into the CTB buffers
- than they can hold.
- You will not stress-test the Asynchronous calls, which
- may not be ready for prime time.
- */
-
- // Necessary includes (uncomment or precompile)
- #include "Connections.h"
- #include "FileTransfers.h"
- #include "CommResources.h"
- #include "CTB.h"
-
- // Private literals
- #define kPrefsType 'PREF' // signature resource is
- #define kAppSignature 'FACE' // registered w/Apple
- #define kClassCM 'cbnd' // replace with your own
- #define rConnClassID 1001
- #define rFirstToolConfigID 2001
-
- // Global variables
- static ConnHandle gConn = nil;
- static Boolean gAsyncCalls, gCTBInited = false;
- static CMFlags gFlags;
- static CMStatFlags gPrevStatus;
- static MenuHandle gConnMenu;
- static ConnectionCompletionUPP gCMCompletion;
- static FileTransferSendUPP gCMWriteProc;
- static FileTransferReceiveUPP gCMReadProc;
-
- //**********************************************************
- //
- // pStrCopy - Copy the value of p1 to p2
- //
- //**********************************************************
- static void pStrCopy(unsigned char *p1, unsigned char *p2)
- {
- register short len = *p2++ = *p1++;
- while (--len >= 0)
- *p2++=*p1++;
- }
-
- //**********************************************************
- //
- // GetOpenFileDir - Gets VRef and ParentDirId for open file
- //
- //**********************************************************
- static OSErr GetOpenFileDir(short openFileRef, short *vRef,
- long *parDirID)
- {
- FCBPBRec pb;
- OSErr theErr;
-
- pb.ioCompletion = nil;
- pb.ioFCBIndx = 0;
- pb.ioVRefNum = 0;
- pb.ioRefNum = openFileRef;
- pb.ioNamePtr = nil;
-
- theErr = PBGetFCBInfo(&pb, false);
-
- *vRef = pb.ioFCBVRefNum;
- *parDirID = pb.ioFCBParID;
-
- return theErr;
- }
-
- //**********************************************************
- //
- // OpenPrefsFile - Opens prefs, creates if not found
- //
- //**********************************************************
- static OSErr OpenPrefsFile(short *myRefNum, Boolean createIt)
- {
- Str255 myFileName;
- long myDirID;
- short myVRef, appResRef;
- OSErr theErr;
- FSSpec mySpec, appDirSpec;
- Boolean found;
-
- *myRefNum = -1;
- found = false;
- pStrCopy("\pHappyFace Prefs", myFileName);
- appResRef = CurResFile();
-
- theErr = GetOpenFileDir(appResRef, &myVRef, &myDirID);
- if (!theErr) {
- theErr = FSMakeFSSpec(myVRef, myDirID, myFileName,
- &appDirSpec);
- if (!theErr) {
- found = true;
- BlockMove(&appDirSpec, &mySpec, sizeof(FSSpec));
- }
- }
-
- if ( (theErr == fnfErr) && createIt ) {
- BlockMove(&appDirSpec, &mySpec, sizeof(FSSpec));
- FSpCreateResFile(&mySpec, kAppSignature, kPrefsType, 0);
- theErr = ResError();
- }
-
- if (!theErr) {
- *myRefNum = FSpOpenResFile(&mySpec, fsRdWrPerm);
- theErr = ResError();
- }
-
- return theErr;
- }
-
- //**********************************************************
- //
- // CMCompetion - Completion routine for the Connection Mgr
- //
- //**********************************************************
- static pascal void CMCompletion(ConnHandle hConn)
- {
- // Should at least check to see if (**hConn).errCode
- // is non-zero. Perhaps use hConn to put any error
- // code in UserData for the main program to flush out.
- }
-
- //**********************************************************
- //
- // CMWriteProc - Sends the data out through the connection
- //
- //**********************************************************
- static pascal OSErr CMWriteProc(Ptr thePtr, long theSize,
- CMFlags flags)
- {
- long count, returnVal;
- EventRecord theEvent;
- CMBufferSizes sizes;
- Ptr p;
- OSErr theErr;
- CMStatFlags status;
-
- returnVal = 0;
-
- if (gConn) {
- p = thePtr;
- count = theSize;// Give other processes time while
- do { // waiting for previous write to finish
- if (gAsyncCalls) do {
- if (theErr = CMStatus(gConn, sizes, &status))
- return theErr;
- if (status & (cmStatusDWPend + cmStatusOpening))
- WaitNextEvent(nullEvent, &theEvent, 1, nil);
- } while (status & (cmStatusDWPend + cmStatusOpening));
- if (theErr = CMWrite(gConn, p, &count, cmData,
- gAsyncCalls, gCMCompletion, -1, flags))
- return theErr;
- if (gAsyncCalls) {
- do { // async calls provide false count
- if (theErr = CMStatus(gConn, sizes, &status))
- return theErr;
- if (status & (cmStatusDWPend + cmStatusOpening))
- WaitNextEvent(nullEvent, &theEvent, 1, nil);
- } while (status & cmStatusDWPend);
- count = (**gConn).asyncCount[cmDataOut];
- }
- p += count; // continue where we left off
- theSize = theSize - count;
- count = theSize;
- } while (count > 0);
- }
- return noErr;
- }
-
- //**********************************************************
- //
- // ConnEvent - Give the Connection a crack at the event
- //
- //**********************************************************
- Boolean ConnEvent(EventRecord *theEvent)
- {
- WindowPtr theWindow;
-
- switch(theEvent->what) {
- case autoKey:
- case keyDown:
- theWindow = FrontWindow();
- break;
- case mouseDown:
- FindWindow(theEvent->where, &theWindow);
- break;
- case updateEvt:
- case activateEvt:
- theWindow = (WindowPtr)theEvent->message;
- break;
- default:
- theWindow = nil; // only pass through above events
- break;
- }
-
- if (!gConn || !theWindow)
- return false;
- else if (gConn != (ConnHandle)GetWRefCon(theWindow))
- return false;
- else {
- CMEvent(gConn, theEvent);
- return true;
- }
- }
-
- //**********************************************************
- //
- // ConnActivate - Passes activate event on to Connection
- //
- //**********************************************************
- void ConnActivate(WindowPtr theWindow, Boolean becomingActive)
- {
- GrafPtr oldPort;
-
- if (gConn && theWindow) {
- GetPort(&oldPort);
- SetPort(theWindow);
- CMActivate(gConn, becomingActive);
- SetPort(oldPort);
- }
- }
-
- //**********************************************************
- //
- // ConnResume - Passes resume event on to Connection
- //
- //**********************************************************
- void ConnResume(WindowPtr theWindow, Boolean resuming)
- {
- GrafPtr oldPort;
-
- if (gConn && theWindow) {
- GetPort(&oldPort);
- SetPort(theWindow);
- CMResume(gConn, resuming);
- SetPort(oldPort);
- }
- }
-
- //**********************************************************
- //
- // putc - Sends a character out through the connection
- //
- //**********************************************************
- OSErr putc(unsigned char *c)
- {
- return CMWriteProc((Ptr)c, 1L, gFlags);
- }
-
- //**********************************************************
- //
- // CMReadProc - Reads data in from the connection
- //
- //**********************************************************
- static pascal OSErr CMReadProc(Ptr thePtr, long *theSize,
- CMFlags *flags)
- // reads *theSize chars into dataPtr
- // synchronous, no matter what gAsyncCalls says;
- // will have to try harder to get the contents of a block
- {
- if (gConn)
- return CMRead(gConn, thePtr, theSize, cmData, false,
- nil, 0, flags);
- else {
- *theSize = 0;
- return noErr;
- }
- }
-
- //**********************************************************
- //
- // getc - gets a character in from the connection
- //
- //**********************************************************
- OSErr getc(unsigned char *c)
- {
- OSErr theErr;
- long theLen;
- CMFlags flags;
-
- theLen = 1;
- theErr = CMReadProc((Ptr)c, &theLen, &flags);
- if (!theLen)
- *c = NULL;
- return theErr;
- }
-
- //**********************************************************
- //
- // InitCTBStuff - Initializes comm toolbox
- //
- //**********************************************************
- static OSErr InitCTB(void)
- {
- short theErr;
-
- if (!gCTBInited) {
- if (theErr = InitCTBUtilities())
- return theErr;
- if (theErr = InitCRM())
- return theErr;
- if (theErr = InitCM())
- return theErr;
- // PowerPC friendly Mixed-Mode stuff follows
- gCMCompletion = (ConnectionCompletionUPP)
- NewConnectionCompletionProc((ProcPtr)CMCompletion);
- gCMWriteProc = (FileTransferSendUPP)
- NewFileTransferSendProc((ProcPtr)CMWriteProc);
- gCMReadProc = (FileTransferReceiveUPP)
- NewFileTransferReceiveProc((ProcPtr)CMReadProc);
- gCTBInited = true;
- }
- return noErr;
- }
-
- //**********************************************************
- //
- // FindTool - Tries to get the choice tool if
- // available, else tries first available.
- //
- //**********************************************************
- static OSErr FindTool(short *procID, Str255 toolName)
- {
- Handle rHand;
- StringHandle sH;
- OSErr theErr;
-
- *procID = -1;
- theErr = noErr;
-
- // Get name of tool last used, and its procID
- rHand = GetResource(kAppSignature, rConnClassID);
- if (rHand) {
- sH = (StringHandle)rHand;
- pStrCopy(*sH, toolName);
- *procID = CMGetProcID(toolName);
- }
- if (*procID == -1 ) {
- // may not actually have a resource file open, or the
- // tool we want is no longer in the extensions folder so
- // get the name and procID of the first tool we find
- if (!(theErr = CRMGetIndToolName(kClassCM, 1, toolName)))
- *procID = CMGetProcID(toolName);
- if (*procID == -1)
- theErr = ResError();
- if (!theErr)
- theErr = kToolNotAvailErr;
- }
- return theErr;
- }
-
- //**********************************************************
- //
- // ReadPrefs - Read prefs for tool of specified class
- //
- //**********************************************************
- static OSErr ReadPrefs(short *procID, Handle *configH)
- {
- short resFileRef;
- Str255 toolName;
- Handle resH;
- OSErr theErr;
-
- theErr = OpenPrefsFile(&resFileRef, kDontCreate);
- // if we can't open prefs, we can still get a tool
- theErr = FindTool(procID, toolName);
- *configH = nil;
-
- if (resFileRef != -1) {
- if (!theErr) {
- // if the resource file was opened, get the
- // configuration string from a resource
- resH = GetNamedResource(kAppSignature, toolName);
- if (resH) {
- *configH = resH;
- HandToHand(configH);
- ReleaseResource(resH);
- } else
- theErr = ResError();
- }
- CloseResFile(resFileRef);
- }
- return theErr; // could be returning kToolNotAvailErr
- }
-
- //**********************************************************
- //
- // WritePrefs - Create/Update connection tool prefs
- //
- //**********************************************************
- static OSErr WritePrefs(ConnHandle hConn)
- {
- Ptr specP;
- Handle specH, resH;
- Str255 tName, tClassName;
- OSErr theErr;
- short resID, resFileRef;
- Size hSize;
- StringHandle sH;
-
- if (!hConn)
- return kNoConnectionErr;
-
- // Get the current tool's name and its config string
- CMGetToolName((**hConn).procID, tName);
- specP = CMGetConfig(hConn);
- if (!specP)
- return kGetConfigErr;
-
- if (theErr = OpenPrefsFile(&resFileRef, kDoCreate))
- return theErr;
-
- // Update/Create resource specifying tool name
- resID = rConnClassID;
- pStrCopy("\pConnection Tool Name", tClassName);
- sH = NewString(tName);
- if (sH) {
- hSize = GetHandleSize((Handle)sH);
- resH = Get1Resource(kAppSignature, resID);
- theErr = ResError();
- if ((!resH) && ((theErr == resNotFound) ||
- (theErr == noErr))) {
- AddResource((Handle)sH, kAppSignature,
- resID, tClassName);
- if (!(theErr = ResError()))
- WriteResource((Handle)sH);
- if (!theErr)
- theErr = ResError();
- ReleaseResource((Handle)sH);
- } else if (resH) {
- SetHandleSize(resH, hSize);
- if (!(theErr = MemError())) {
- BlockMove(*sH, *resH, hSize);
- ChangedResource(resH);
- if (!(theErr = ResError()))
- WriteResource(resH);
- if (!theErr)
- theErr = ResError();
- }
- ReleaseResource(resH);
- }
- DisposeHandle((Handle)sH);
- } else
- theErr = MemError();
-
- // Update/Create resource with the tool config string
- if (!theErr) {
- hSize = GetPtrSize(specP);
- resH = Get1NamedResource(kAppSignature, tName);
- theErr = ResError();
- if ( (!resH) && ((theErr == resNotFound) ||
- (theErr == noErr)) ) {
- theErr = noErr;
- specH = NewHandle(hSize);
- if (specH) {
- BlockMove(specP, *specH, hSize);
- do
- resID = UniqueID(kAppSignature);
- while (resID <= rFirstToolConfigID);
- AddResource(specH, kAppSignature, resID, tName);
- if (!(theErr = ResError()))
- WriteResource(specH);
- if (!theErr)
- theErr = ResError();
- ReleaseResource(specH);
- DisposeHandle(specH);
- } else
- theErr = MemError();
- } else if (resH) {
- SetHandleSize(resH, hSize);
- if (!(theErr = MemError())) {
- BlockMove(specP, *resH, hSize);
- ChangedResource(resH);
- if (!(theErr = ResError()))
- WriteResource(resH);
- if (!theErr)
- theErr = ResError();
- }
- ReleaseResource(resH);
- }
- }
-
- DisposePtr(specP);
- CloseResFile(resFileRef);
-
- return theErr;
- }
-
- //**********************************************************
- //
- // ChooseConn - Poses Connection Settings dialog
- // allowing the user to choose and configure a tool
- //
- //**********************************************************
- OSErr ChooseConn(void)
- {
- Point where;
- short result;
- OSErr returnVal;
-
- returnVal = kNothingChosenErr;
-
- if (gConn) {
- SetPt(&where, 20, 40);
- HUnlock((Handle)gConn);
- result = CMChoose(&gConn, where, nil);
- HLock((Handle)gConn);
- switch (result) {
- case chooseOKMinor:
- case chooseOKMajor:
- returnVal = noErr;
- break;
- case chooseCancel:
- returnVal = kNothingChosenErr;
- break;
- case chooseAborted:
- case chooseDisaster:
- case chooseFailed:
- default:
- returnVal = result;
- break;
- }
- }
- return returnVal;
- }
-
- //**********************************************************
- //
- // NewConn - Init connection mgr for use
- //
- //**********************************************************
- OSErr NewConn(Boolean async, Boolean initCTB, Boolean prompt)
- {
- short procID;
- Handle configH;
- CMBufferSizes sizes;
- OSErr theErr;
- Boolean newTool;
-
- if (gConn)
- return kAlreadyInitErr;
-
- if (initCTB)
- if (theErr = InitCTB())
- return theErr;
-
- // Find user's preferred tool and config string
- // or get defaults if blown or do not exist
- theErr = ReadPrefs(&procID, &configH);
- if (theErr == kToolNotAvailErr) {
- newTool = true;
- theErr = noErr; // if problem is tool changed,
- } else if (theErr) // make note, else pitch
- return theErr;
- else
- newTool = false;
-
- if (procID == -1)
- return kNoConnToolErr;
-
- sizes[cmDataIn] = 0; // Asking for zero means leave
- sizes[cmDataOut] = 0; // buffer size up to tool
- sizes[cmCntlIn] = 0;
- sizes[cmCntlOut] = 0;
- sizes[cmAttnIn] = 0;
- sizes[cmAttnOut] = 0;
-
- // Get a new Connection Tool record
- // and set config string as saved
- gConn = CMNew(procID, cmData, sizes, 0, 0);
- if (!gConn) {
- if (configH)
- DisposeHandle(configH);
- return kCMNewErr;
- } else if (configH) {
- HLock(configH); // don't let memory manager move
- CMSetConfig(gConn, *configH); // dereferenced pointer
- HUnlock(configH);
- DisposeHandle(configH);
- }
- MoveHHi((Handle)gConn);
- HLock((Handle)gConn); // lock to access data at interrupt
-
- // pose Connection Settings dialog if desired or desired
- // tool or configuration string was not available
- theErr = noErr;
- if (prompt || newTool || !configH)
- if (ChooseConn())
- theErr = kNothingChosenErr;
-
- if (theErr) {
- if (gConn) // if ChooseDisaster, gConn already
- CMDispose(gConn); // disposed & nil
- gConn = nil;
- return theErr;
- }
-
- gFlags = false;
- gAsyncCalls = async;
- gPrevStatus = 0xFFFFFFFF;
- gConnMenu = GetMenu(rConnMenuID);
-
- return noErr;
- }
-
- //**********************************************************
- //
- // OpenConn - Open connection with other copy
- //
- //**********************************************************
- OSErr OpenConn(void)
- {
- if (!gConn)
- return kNoConnectionErr;
-
- return CMOpen(gConn, gAsyncCalls, gCMCompletion, -1);
- }
-
- //**********************************************************
- //
- // ListenConn - Listen for connection with other copy
- //
- //**********************************************************
- OSErr ListenConn(void)
- {
- CMBufferSizes sizes;
- CMStatFlags status;
- OSErr theErr;
-
- if (gConn) {
- theErr = CMStatus(gConn, sizes, &status);
- if (!theErr) { // async even if other calls are sync
- if (!(status & cmStatusListenPend))
- theErr = CMListen(gConn, kAsync, gCMCompletion, -1);
- else
- CMAbort(gConn); // close opened or listening
- }
- } else
- theErr = kNoConnectionErr;
-
- return theErr;
- }
-
- //**********************************************************
- //
- // CloseConn - Close connection with other copy
- //
- //**********************************************************
- OSErr CloseConn(void)
- {
- OSErr theErr;
- CMStatFlags status;
- CMBufferSizes sizes;
-
- if (!gConn) // close only exists
- return kNoConnectionErr;// and open or opening
-
- // close the connection, but only if it's open or trying to open
- theErr = CMStatus(gConn, sizes, &status);
- if (status & (cmStatusOpen + cmStatusOpening))
- theErr = CMClose(gConn, gAsyncCalls, gCMCompletion,
- -1, false);
-
- return theErr;
- }
-
- //**********************************************************
- //
- // DisposeConn - Dispose CTB connection
- //
- //**********************************************************
- OSErr DisposeConn(void)
- {
- if (!gConn) // dispose only if exists
- return kNoConnectionErr;
-
- // write resulting new preferences
- WritePrefs(gConn); // may want to complain
-
- CloseConn(); // don't care about error, only
- HUnlock((Handle)gConn); // calling in case open
- CMDispose(gConn);
- gConn = nil;
-
- return noErr;
- }
-
- //**********************************************************
- //
- // IdleConn - Performs event loop idle functions
- // also tells caller if online
- //
- //**********************************************************
- OSErr IdleConn(Boolean *online)
- {
- CMStatFlags status;
- CMBufferSizes sizes;
- OSErr theErr;
-
- theErr = noErr;
-
- if (gConn) {
- CMIdle(gConn); // Accept if we're not already busy
- if (!(theErr = CMStatus(gConn, sizes, &status))) {
- if (status & cmStatusIncomingCallPresent)
- CMAccept(gConn, true);
- if (status != gPrevStatus) {
- *online = false;
- DisableItem(gConnMenu, rConnMItem);
- DisableItem(gConnMenu, rDiscMItem);
- DisableItem(gConnMenu, rListenMItem);
- DisableItem(gConnMenu, rSettingsMItem);
- if (status & cmStatusOpen) {
- EnableItem(gConnMenu, rDiscMItem);
- *online = true;
- } else if (status & cmStatusListenPend) {
- EnableItem(gConnMenu, rListenMItem);
- SetItem(gConnMenu, rListenMItem,
- "\pStop Listening");
- } else {
- EnableItem(gConnMenu, rConnMItem);
- EnableItem(gConnMenu, rListenMItem);
- EnableItem(gConnMenu, rSettingsMItem);
- SetItem(gConnMenu, rListenMItem, "\pListen");
- }
- gPrevStatus = status;
- }
- }
- } else {
- DisableItem(gConnMenu, rConnMItem);
- DisableItem(gConnMenu, rDiscMItem);
- DisableItem(gConnMenu, rListenMItem);
- DisableItem(gConnMenu, rSettingsMItem);
- }
-
- return theErr;
- }
-
- //**********************************************************
- //
- // DoConnMenu - Do action that corresponds to menu item
- //
- //**********************************************************
- void DoConnMenu(short itemNumber)
- {
- OSErr theErr; // you will want to respond to errors
-
- switch(itemNumber) {
- case rConnMItem:
- if (OpenConn())
- SysBeep(1);
- break;
- case rDiscMItem:
- if (CloseConn())
- SysBeep(1);
- break;
- case rListenMItem:
- if (ListenConn())
- SysBeep(1); // could be cmNotSupported
- break;
- case rSettingsMItem:
- if (theErr = ChooseConn())
- if (theErr != kNothingChosenErr)
- SysBeep(1);
- break;
- default:
- break;
- }
- }
-